package eu.trojanbug.spring.security.gae;

import org.springframework.core.Ordered;
import org.springframework.security.Authentication;
import org.springframework.security.ConfigAttribute;
import org.springframework.security.ConfigAttributeDefinition;
import org.springframework.security.vote.AccessDecisionVoter;

import java.util.Iterator;

public class ConfigAttributeNameBasedVoter implements AccessDecisionVoter,
		Ordered {

	public static int DEFAULT_ORDER = Ordered.LOWEST_PRECEDENCE;

	private String processedConfigAttributePrefix = "ROLE_";
	private int order = DEFAULT_ORDER;

	public String getProcessedConfigAttributePrefix() {
		return processedConfigAttributePrefix;
	}

	/**
	 * Allows the default role prefix of <code>ROLE_</code> to be overriden. May
	 * be set to an empty value, although this is usually not desireable.
	 * 
	 * @param processedConfigAttributePrefix
	 *            the new prefix
	 */
	public void setProcessedConfigAttributePrefix(String rolePrefix) {
		this.processedConfigAttributePrefix = rolePrefix;
	}

	public boolean supports(ConfigAttribute attribute) {
		if ((attribute.getAttribute() != null)
				&& (attribute.getAttribute().startsWith(getProcessedConfigAttributePrefix())
				|| attribute.getAttribute().startsWith("NOT_"+getProcessedConfigAttributePrefix()))
		) {
			return true;
		} else {
			return false;
		}
	}

	/**
	 * This implementation supports any type of class, because it does not query
	 * the presented secure object.
	 * 
	 * @param clazz
	 *            the secure object
	 * 
	 * @return always <code>true</code>
	 */
	public boolean supports(Class clazz) {
		return true;
	}

	public int vote(Authentication authentication, Object object,
			ConfigAttributeDefinition config) {
		int result = ACCESS_ABSTAIN;
		Iterator iter = config.getConfigAttributes().iterator();

		while (iter.hasNext()) {
			ConfigAttribute attribute = (ConfigAttribute) iter.next();

			if (this.supports(attribute)) {

				if (attribute.getAttribute().startsWith("NOT_")) {
					String baseAttribute = attribute.getAttribute().substring(
							"NOT_".length());
					result = ACCESS_GRANTED;

					// Attempt to find a matching granted authority, if found, deny access, as it is interpreted as forbidded
					for (int i = 0; i < authentication.getAuthorities().length; i++) {
						if (baseAttribute.equals(authentication
								.getAuthorities()[i].getAuthority())) {
							return ACCESS_DENIED;
						}
					}
				} else {
					result = ACCESS_DENIED;

					// Attempt to find a matching granted authority
					for (int i = 0; i < authentication.getAuthorities().length; i++) {
						if (attribute.getAttribute().equals(
								authentication.getAuthorities()[i]
										.getAuthority())) {
							return ACCESS_GRANTED;
						}
					}
				}
			}
		}

		return result;
	}

	public void setOrder(int order) {
		this.order = order;
	}

	public int getOrder() {
		return order;
	}

}
